package com.simpou.commons.web.mbean;

import com.simpou.commons.utils.file.DefaultPropertiesLoader;
import com.simpou.commons.utils.file.FileHelper;
import com.simpou.commons.utils.file.PropertiesLoader;
import com.simpou.commons.utils.string.Strings;
import com.simpou.commons.web.Parameters;
import lombok.extern.apachecommons.CommonsLog;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.context.Flash;
import javax.faces.model.SelectItem;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ResourceBundle;

/**
 * Utilitários para Managed Beans.
 *
 * @author <a href="mailto:jonas.pereira@concert.com.br">Jonas Pereira</a>
 * @version 2011-11-07
 * @since 2011-11-04
 */
@CommonsLog
public abstract class AbstractMBean implements Serializable {

    private static final String pagesRelPath;

    private static final String globalMsgsId;

    private static final String bundleEnumPreffix;

    /**
     * Tradutor para mensagens de negócio do sistema.
     */
    private static final ResourceBundle bundle;


    static {
        PropertiesLoader props;
        try {
            final File propsFile = FileHelper.getResourceFile(Parameters.FILE_PROPS_PATH);
            props = new DefaultPropertiesLoader(propsFile.getAbsolutePath());
        } catch (IOException ex) {
            props = null;
        }
        globalMsgsId = null;//":"+props.get("el.msg.id.global");
        final String bundleName;
        if (props == null) {
            log.warn("Resource file "+Parameters.FILE_PROPS_PATH+" not found. Using default properties.");
            bundleName = "Bundle";
            bundleEnumPreffix = "enum_";
            pagesRelPath = "view";
        }else{
            bundleName  = props.get("bundle.name");
            bundleEnumPreffix = props.get("bundle.preffix.enum");
            pagesRelPath = props.get("app.path.pages");
        }
        try{
            bundle = ResourceBundle.getBundle(bundleName);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

//    /**
//     * Exigência do WELD. Use outro construtor.
//     */
//    public AbstractMBean() {
//        throw new UnsupportedOperationException("Use other constructor.");
//    }
//    
//    public AbstractMBean(final ResourceBundle bundle) {
//        this.bundle = bundle;
//    }


    /**
     * <p>getEnumList.</p>
     *
     * @param clasz Classe do enum.
     * @return Lista de todos elementos de um enum traduzidos para ser usada em componentes visuais de seleção. Não é
     * necessário utilizar "converters", o mapeamento chave/valor já é feito.
     */
    protected SelectItem[] getEnumList(final Class<? extends Enum<?>> clasz) {
        Enum[] enums = clasz.getEnumConstants();
        SelectItem[] items = new SelectItem[enums.length];
        Enum<?> enumItem;

        final String preffix = bundleEnumPreffix + clasz.getName().replaceAll("\\.", "_") + "_";

        for (int i = 0; i < enums.length; i++) {
            enumItem = enums[i];

            items[i] = new SelectItem(enumItem,
                    bundle.getString(preffix + enumItem.name()));
        }

        return items;
    }

    /**
     * Exibe uma mensagem na tela web para um componente visual que tenha um "h:message" associado a ele.
     *
     * @param key      Chave para um mensagem no arquivo de bundle.
     * @param field    Identificador do componente.
     * @param severity Severidade ou nível de alerta.
     */
    protected void addMessageToField(final String key, final String field,
                                     final FacesMessage.Severity severity, final Object... params) {
        addMessageToFieldNoBundle(bundle.getString(key), field, severity, params);
    }

    /**
     * Exibe uma mensagem na tela web para um componente visual que tenha um "h:message" associado a ele.
     *
     * @param msg      Mensagem.
     * @param field    Identificador do componente.
     * @param severity Severidade ou nível de alerta.
     */
    protected void addMessageToFieldNoBundle(final String msg,
                                             final String field, final FacesMessage.Severity severity, final Object... params) {
        FacesContext context = FacesContext.getCurrentInstance();
        FacesMessage message;
        message = new FacesMessage(Strings.replaceParams(msg, params));
        message.setSeverity(severity);
        context.addMessage(field, message);
    }

    /**
     * Exibe uma mensagem na tela web para o componente "h:message" global do sistema.
     *
     * @param msg      Mensagem.
     * @param severity Severidade ou nível de alerta.
     */
    protected void showMessage(final String msg,
                               final FacesMessage.Severity severity, final Object... params) {
        addMessageToField(msg, globalMsgsId, severity, params);
    }

    /**
     * Exibe uma mensagem do tipo INFO na tela web para o componente "h:message" global do sistema.
     *
     * @param msg Mensagem.
     */
    protected void showInfo(final String msg, final Object... params) {
        showMessage(msg, FacesMessage.SEVERITY_INFO, params);
    }

    /**
     * Exibe uma mensagem do tipo ERROR na tela web para o componente "h:message" global do sistema.
     *
     * @param msg Mensagem.
     */
    protected void showError(final String msg, final Object... params) {
        showMessage(msg, FacesMessage.SEVERITY_ERROR, params);
    }

    /**
     * Para usar, insira dados no escopo, faça o "flash redirect" e recupere os dados na próxima requisição. É um escopo
     * maior que o da requisição e menor que o de visão do JSF, ou de conversação no caso do CDI.
     *
     * @return Pseudo-escopo "Flash" do JSF.
     */
    protected Flash getFlash() {
        return FacesContext.getCurrentInstance().getExternalContext().getFlash();
    }

    /**
     * Use para redirecionamentos de páginas caso esteja usando o pseudo-escopo "flash" do JSF para um MBean.
     *
     * @param page a {@link java.lang.String} object.
     * @return a {@link java.lang.String} object.
     */
    protected String flashRedirect(final String page) {
        return page + ".xhtml?faces-redirect=true";
    }

    /**
     * <p>getFacesRequest.</p>
     *
     * @return Request.
     */
    protected HttpServletRequest getFacesRequest() {
        return (HttpServletRequest) getFacesContext().getExternalContext()
                .getRequest();
    }

    /**
     * <p>getFacesContext.</p>
     *
     * @return FacesContext.
     */
    protected FacesContext getFacesContext() {
        return FacesContext.getCurrentInstance();
    }

    /**
     * Redireciona para uma página.
     *
     * @param relPage Caminho da página de destino relativo ao diretório base de páginas.
     * @return Caminho completo da página de destino.
     */
    protected String redirect(final String relPage) {
        return pagesRelPath + "/" + relPage;
    }
}
